using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;

using LumiSoft.Net.IO;

namespace LumiSoft.Net.MIME.Header
{
    /// <summary>
    /// Represents MIME entity header field collection.
    /// </summary>
    public class MIME_HeaderFieldCollection : IEnumerable
    {
        private List<MIME_HeaderField> m_pCollection = null;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public MIME_HeaderFieldCollection()
        {
            m_pCollection = new List<MIME_HeaderField>();
        }


        #region method Add

        /// <summary>
        /// Add a new header field from the header field string. Syntax: {headerFieldName: value}
        /// </summary>
        /// <param name="field">Header filed string.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>field</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public void Add(string field)
        {
            if(field == null){
                throw new ArgumentNullException("field");
            }
            if(field == "" || field.IndexOf(':') == -1){
                throw new ArgumentException("Argument 'field' value '" + field + "' is not valid header field string.");
            }

            string[] name_value = field.Split(new char[]{':'},2);
            Add(name_value[0],name_value[1]);
        }

        /// <summary>
        /// Adds a new header field with specified name and value to the end of the collection.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <param name="value">Field value.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> or <b>value</b> is null.</exception>
        public void Add(string name,string value)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }
            if(value == null){
                throw new ArgumentNullException("value");
            }

            m_pCollection.Add(new MIME_HeaderField(name,value));
        }

        /// <summary>
        /// Adds a new header field with specified name and value to the end of the collection.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <param name="value">Field value.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> or <b>value</b> is null.</exception>
        public void Add(string name,MIME_HeaderFieldValue value)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }
            if(value == null){
                throw new ArgumentNullException("value");
            }

            m_pCollection.Add(new MIME_HeaderField(name,value));
        }
                
        /// <summary>
        /// Adds specified header field to the end of the collection.
        /// </summary>
        /// <param name="field">Header field.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>field</b> is null.</exception>
        public void Add(MIME_HeaderField field)
        {
            if(field == null){
                throw new ArgumentNullException("field");
            }

            m_pCollection.Add(field);
        }
                
        #endregion

        #region method Insert

        /// <summary>
        /// Inserts a new header field into the collection at the specified location.
        /// </summary>
        /// <param name="index">The location in the collection where you want to add the header field.</param>
        /// <param name="field">Header field.</param>
        /// <exception cref="IndexOutOfRangeException">Is raised when <b>index</b> is out of range.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>field</b> is null.</exception>
        public void Insert(int index,MIME_HeaderField field)
        {
            if(field == null){
                throw new ArgumentNullException("field");
            }

            m_pCollection.Insert(index,field);
        }

        #endregion

        #region method Remove

        /// <summary>
        /// Removes specified header field from the collection.
        /// </summary>
        /// <param name="field">Header field.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>field</b> is null.</exception>
        public void Remove(MIME_HeaderField field)
        {
            if(field == null){
                throw new ArgumentNullException("field");
            }

            m_pCollection.Remove(field);
        }

        /// <summary>
        /// Removes header field at the specified index from the collection.
        /// </summary>
        /// <param name="index">The index of the header field to remove.</param>
        /// <exception cref="IndexOutOfRangeException">Is raised when <b>index</b> is out of range.</exception>
        public void Remove(int index)
        {
            m_pCollection.RemoveAt(index);
        }

        #endregion

        #region method RemoveFirst

        /// <summary>
        /// Removes first(top-most) header field with rhe specified name.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public void RemoveFirst(string name)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            foreach(MIME_HeaderField h in m_pCollection){
                if(h.Name.ToLower() == name.ToLower()){
                    m_pCollection.Remove(h);
                    break;
                }
            }
        }

        #endregion

        #region method Clear

        /// <summary>
        /// Removes all items from the collection.
        /// </summary>
        public void Clear()
        {
            m_pCollection.Clear();
        }

        #endregion

        #region mehtod Contains

        /// <summary>
        /// Gets if the collection contains specified header field.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <returns>Returns true if the specified header field exists in the collection, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public bool Contains(string name)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            return GetFirst(name) != null;
        }

        /// <summary>
        /// Gets if the collection contains specified header field.
        /// </summary>
        /// <param name="field">Header field.</param>
        /// <returns>Returns true if the specified header field exists in the collection, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>field</b> is null.</exception>
        public bool Contains(MIME_HeaderField field)
        {
            if(field == null){
                throw new ArgumentNullException("field");
            }

            return m_pCollection.Contains(field);
        }

        #endregion

        #region method Get

        /// <summary>
        /// Gets header fields with specified name.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <returns>Returns header fields with specified name.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public MIME_HeaderField[] Get(string name)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            List<MIME_HeaderField> fields = new List<MIME_HeaderField>();
			foreach(MIME_HeaderField h in m_pCollection){
				if(h.Name.ToLower() == name.ToLower()){
					fields.Add(h);
				}
			}

            return fields.ToArray();
        }

        #endregion

        #region method GetFirst

        /// <summary>
        /// Gets first header field with the specified name, returns null if specified field doesn't exist.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <returns>Returns specified header field or null if specified field doesn't exist.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public MIME_HeaderField GetFirst(string name)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            foreach(MIME_HeaderField h in m_pCollection){
				if(h.Name.ToLower() == name.ToLower()){
					return h;
				}
			}

			return null;
        }

        #endregion

        #region method GetFirstValueString

        /// <summary>
        /// Searches first header field with te specified name and gets it's value. Returns null if header field does not exist.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <returns>Returns header field value or null if header field does not exist.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public string GetFirstValueString(string name)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            MIME_HeaderFieldValue value = GetFirstValue(name);
            if(value != null){
                return value.ValueString;
            }

            return null;
        }

        #endregion

        #region method GetFirstValue

        /// <summary>
        /// Searches first header field with te specified name and gets it's value. Returns null if header field does not exist.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <returns>Returns header field value or null if header field does not exist.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public MIME_HeaderFieldValue GetFirstValue(string name)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            MIME_HeaderField hf = GetFirst(name);
            if(hf != null){
                return hf.Value;
            }

            return null;
        }

        #endregion

        #region method SetFirstValue

        /// <summary>
        /// Searches first header field with te specified name and sets it's value or if no such field, it will be appended to the end of the collection.
        /// If <b>value</b> is null, then first found header field will be removed.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <param name="value">Field value.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public void SetFirstValue(string name,string value)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            if(value == null){
                RemoveFirst(name);
            }
            else{
                MIME_HeaderField hf = GetFirst(name);
                if(hf != null){
                    hf.Value.ValueString = value;
                }
                else{
                    Add(name,value);
                }
            }
        }

        /// <summary>
        /// Searches first header field with te specified name and sets it's value or if no such field, it will be appended to the end of the collection.
        /// If <b>value</b> is null, then first found header field will be removed.
        /// </summary>
        /// <param name="name">Field name.</param>
        /// <param name="value">Field value.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>name</b> is null.</exception>
        public void SetFirstValue(string name,MIME_HeaderFieldValue value)
        {
            if(name == null){
                throw new ArgumentNullException("name");
            }

            if(value == null){
                RemoveFirst(name);
            }
            else{
                MIME_HeaderField hf = GetFirst(name);
                if(hf != null){
                    hf.Value = value;
                }
                else{
                    Add(name,value);
                }
            }
        }

        #endregion


        #region method ParseFromStream

        /// <summary>
        /// Parses header from the specified stream.
        /// </summary>
        /// <param name="stream">Stream from where to parse header. Parsing starts from stream current position.</param>
        public void ParseFromStream(Stream stream)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            int         countReaded = 0;
            byte[]      buffer      = new byte[8000];
            string      headerField = "";
            SmartStream smartStream = new SmartStream(stream,false);
            while(true){
                string line = smartStream.ReadLineString(buffer,0,buffer.Length,out countReaded);

                // We readed whole header.
                if(string.IsNullOrEmpty(line)){
                    // We have pending header field, parse it.
                    if(!string.IsNullOrEmpty(headerField)){
                        Add(headerField);
                    }
                    return;
                }
                // Previous header field continues.
                else if(char.IsWhiteSpace(line[0])){
                    headerField += line + "\r\n";
                }
                // We have new header field.
                else{
                    // We have pending header field, parse it.
                    if(!string.IsNullOrEmpty(headerField)){
                        Add(headerField);
                    }
                    
                    headerField = line + "\r\n";
                }
            }
        }

        #endregion

        #region method ToFile

        /// <summary>
        /// Stores header to the specified file.
        /// </summary>
        /// <param name="file">File name with path where to store MIME header.</param>
        /// <param name="addSeparator">Specifies if header separator(CRLF) is added.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>file</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public void ToFile(string file,bool addSeparator)
        {
            if(file == null){
                throw new ArgumentNullException("file");
            }
            if(file == ""){
                throw new ArgumentException("Argument 'file' value must be specified.");
            }

            using(FileStream fs = File.Create(file)){
                ToStream(fs,addSeparator);
            }
        }

        #endregion

        #region method ToStream

        /// <summary>
        /// Stores header to the specified stream.
        /// </summary>
        /// <param name="stream">Stream where to store MIME header. Storing starts from stream current position.</param>
        /// <param name="addSeparator">Specifies if header separator(CRLF) is added.</param>
        /// <exception cref="ArgumentNullException">Is raised when when <b>stream</b> is null.</exception>
        public void ToStream(Stream stream,bool addSeparator)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
                        
            byte[] header = Encoding.UTF8.GetBytes(this.ToString(addSeparator));
            stream.Write(header,0,header.Length);
        }

        #endregion

        #region override method ToString

        /// <summary>
        /// Returns MIME header as string.
        /// </summary>
        /// <returns>Returns MIME header as string.</returns>
        public override string ToString()
        {
            return ToString(false);
        }

        /// <summary>
        /// Returns MIME header as string.
        /// </summary>
        /// <param name="addSeparator">Specifies if header separator(CRLF) is added.</param>
        /// <returns>Returns MIME header as string.</returns>
        public string ToString(bool addSeparator)
        {
            StringBuilder retVal = new StringBuilder();
            foreach(MIME_HeaderField hf in m_pCollection){
                retVal.Append(hf.ToString());
            }
            if(addSeparator){
                retVal.Append("\r\n");
            }

            return retVal.ToString();
        }

        #endregion


        #region interface IEnumerator

        /// <summary>
		/// Gets enumerator.
		/// </summary>
		/// <returns>Returns IEnumerator interface.</returns>
		public IEnumerator GetEnumerator()
		{
			return m_pCollection.GetEnumerator();
		}

		#endregion

        #region Properties Implementation

        /// <summary>
        /// Gets number of itemsin the collection.
        /// </summary>
        public int Count
        {
            get{ return m_pCollection.Count; }
        }        

        #endregion

    }
}
